﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Drawing.Imaging;
using System.Globalization;
using FotoAnotSharp.Anotator;


namespace FotoAnotSharp
{
    /// <summary>
    ///  This class create SVG output given by project instructions:
    /// 1.necessary XML headers
    /// 2.anotated items compound from id and note.
    /// 3.image in base64
    /// 4.coordinates (x,y) of polygons points
    /// Using Xml.Linq
    /// </summary>
    class XmlOutputWriter
    {
        private XmlWriter mWriter;
        private int width;
        private int height;
        private IEnumerable<AnotPolygonTree> tree;
        private IEnumerable<GateAnotaionComponent.AnotatedObject> annotatedObjects;
        private Image mImage;
        private static readonly XNamespace gate = "http://www.fi.muni.cz/~oslejsek/GATE/gate.owl#";
        private static readonly XNamespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
        private static readonly XNamespace owl = "http://www.w3.org/2002/07/owl#";
        private static readonly XNamespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
        private static readonly XNamespace xlink = @"http://www.w3.org/1999/xlink";
        private static readonly XNamespace svg = @"http://www.w3.org/2000/svg";

        XmlOutputWriter()
        { }

        /// <summary>
        /// Export image with annotation
        /// </summary>
        /// <param name="annotatedObjects">annotated objects</param>
        /// <param name="image">input image</param>
        /// <returns>svg in string</returns>
        public static void Write(IEnumerable<AnotPolygonTree> newTree, IEnumerable<FotoAnotSharp.GateAnotaionComponent.AnotatedObject> newAnnotatedObjects, Image image, XmlWriter writeTo)
        {
            XmlOutputWriter writer = new XmlOutputWriter();
            writer.mWriter = writeTo;
            writer.tree = newTree;
            writer.annotatedObjects = newAnnotatedObjects;

            writer.mImage = image;
            writer.width = image.Width;
            writer.height = image.Height;

            /*MemoryStream stream = new MemoryStream();
            image.Save(stream, ImageFormat.Jpeg);
            byte[] byteImage = stream.ToArray();
            writer.imageBase64 = Convert.ToBase64String(byteImage);*/

            writer.GenerateSVG();
        }

        /// <summary>
        /// Create svg from image in base64 and annotated objects
        /// </summary>
        /// <returns>svg in string</returns>
        private void GenerateSVG()
        {
            //WriteHeader();

            XElement graphicalObject = new XElement(gate + "GraphicalObject",
                        new XAttribute(rdf + "ID", "GATE_GRAPHICAL_CONTENT"));

            XElement g = new XElement(svg + "g",
                new XAttribute("id", "main"));

            XElement rdfObject =
                new XElement(rdf + "RDF",
                    new XAttribute(XNamespace.Xmlns + "gate", "http://www.fi.muni.cz/~oslejsek/GATE/gate.owl#"),
                    new XAttribute(XNamespace.Xmlns + "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
                    new XAttribute(XNamespace.Xmlns + "owl", "http://www.w3.org/2002/07/owl#"),
                    new XAttribute(XNamespace.Xmlns + "rdfs", "http://www.w3.org/2000/01/rdf-schema#"),
                    new XElement(owl + "Ontology",
                        new XAttribute(rdf + "About", ""),
                        new XElement(owl + "imports",
                            new XAttribute(rdf + "resource", "http://www.fi.muni.cz/~oslejsek/GATE/gate.owl"))),
                    graphicalObject);

            XElement metaElem = new XElement(svg + "metadata",
                new XAttribute("id", "GATE_ANNOTATION_METADATA"),
                rdfObject);

            //generuje vsetky hlavne objekty v elemente GRAPHICAL OBJECT
            GenerateGraphicalObjectElement(graphicalObject, rdfObject);


            XElement parentGroup = CreateParentGroup(g);

            byte[] imageBytes;
            using (MemoryStream stream = new MemoryStream())
            {
                mImage.Save(stream, ImageFormat.Png);
                imageBytes = stream.ToArray();
            }

            XElement image = new XElement(svg + "image",
                new XAttribute("x", "0"),
                new XAttribute("y", "0"),
                new XAttribute("transform", "matrix(1 0 0 1 0 0)"),
                new XAttribute("width", width),
                new XAttribute("height", height),
                new XAttribute(XNamespace.Xmlns + "xlink", @"http://www.w3.org/1999/xlink"),
                new XAttribute(xlink + "href", "data:image/png;base64," + Convert.ToBase64String(imageBytes)),
                new XAttribute(xlink + "type", "simple"),
                new XAttribute(xlink + "actuate", "onLoad"),
                new XAttribute(xlink + "show", "embed")
                );


            g.Add(image);

            g = CreatePolygons(g, tree);
         
            mWriter.WriteDocType("svg", "-//W3C//DTD SVG 1.0//EN", "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd", null);
            mWriter.WriteStartElement("svg", "http://www.w3.org/2000/svg");
            mWriter.WriteAttributeString("width", width.ToString());
            mWriter.WriteAttributeString("height", height.ToString());
            mWriter.WriteAttributeString("xmlns","xlink", "", "http://www.w3.org/1999/xlink");
            mWriter.WriteAttributeString("contentStyleType", "text/css");
            mWriter.WriteAttributeString("viewBox", string.Format("0 0 {0} {1}", width, height));
            mWriter.WriteAttributeString("version", "1.0");

            metaElem.WriteTo(mWriter);
            parentGroup.WriteTo(mWriter);

            //return (header + metadata + parentGroup.ToString() + @"</svg>");

            mWriter.WriteEndElement(); //svg
        }

        private static XElement CreateParentGroup(XElement g)
        {

            string style = ".str0 {stroke:#1F1A17;stroke-width:0.230973}    .fil0 {fill:none}";
            XElement parentGroup = new XElement(svg + "g",
                new XAttribute("id", "GATE_GRAPHICAL_CONTENT"),
                new XElement(svg +"defs",
                    new XElement(svg + "style",
                        new XAttribute("type", "text/css"),
                        new XAttribute(XNamespace.Xml + "space", "preserve"),
                        new XCData(style)
                        )
                    ),
                g);
            return parentGroup;
        }

        private void CreateTree(XElement rdfElem, IEnumerable<AnotPolygonTree> children)
        {
            foreach (var item in children)
            {
                var query = annotatedObjects.Where(x => x.Polygon.Id == item.Polygon.Id).First();
                string queryName = query.GateObject.Name;
                string fragmentName = query.GateObject.FragmentName;

                var labels = query.Labels.Select(x =>
                    new XElement(rdfs + "comment",
                        new XAttribute(XNamespace.Xml + "lang", x.Key),
                        x.Value)
                        );

                XElement parent = new XElement(gate + queryName,
                        new XAttribute(rdf + "ID", item.Polygon.Id)
                        );
                if (item.Nodes.ToList().Count > 0)
                {
                    foreach (var node in item.Nodes)
                    {
                        XElement elem = new XElement(gate + "composedOf",
                            new XAttribute(rdf + "resource", fragmentName)
                            );
                        parent.Add(elem);
                    }
                    rdfElem.Add(parent);

                    CreateTree(rdfElem, item.Nodes);
                }
                else
                {
                    rdfElem.Add(parent);
                }
                parent.Add(labels);
            }
        }

        private static XElement CreatePolygon(GateAnotaionComponent.AnotatedObject annotatedObject)
        {
            string name = annotatedObject.GateObject.Name;
            string points = string.Empty;

            foreach (var point in annotatedObject.Polygon.Points)
            {
                points += string.Format(CultureInfo.InvariantCulture, "{0},{1} ", point.X, point.Y);
            }

            XElement polygon = new XElement(svg + "polygon",
                new XAttribute("id", annotatedObject.Polygon.Id),
                // comment for testing
                new XAttribute("style", "visibility:hidden"),
                new XAttribute("class", "fil0 str0"),
                new XAttribute("points", points)
                );
            return polygon;
        }

        private XElement CreatePolygons(XElement parent, IEnumerable<AnotPolygonTree> myTree)
        {
            foreach (var item in myTree)
            {
                var graphicalObject = annotatedObjects.Where(x => x.Polygon.Id == item.Polygon.Id).First();
                XElement polygon = CreatePolygon(graphicalObject);

                if (item.Nodes.ToList().Count > 0)
                {
                    XElement parentGroup = new XElement(svg + "g");
                    XElement childGroup = new XElement(svg + "g");

                    parentGroup.Add(polygon);
                    parentGroup.Add(childGroup);

                    parent.Add(parentGroup);
                    CreatePolygons(childGroup, item.Nodes);
                }
                else
                {
                    parent.Add(polygon);
                }
            }

            return parent;
        }

        private void GenerateGraphicalObjectElement(XElement graphicalObject, XElement rdfObject)
        {
            IList<AnotPolygonTree> children = new List<AnotPolygonTree>();
            foreach (var node in tree)
            {
                var query = annotatedObjects.Where(x => x.Polygon.Id == node.Polygon.Id).Select(y => y.GateObject.FragmentName).First();
                XElement elem = new XElement(gate + "composedOf",
                    new XAttribute(rdf + "resource", query)
                    );
                graphicalObject.Add(elem);
                children.Add(node);
            }
            CreateTree(rdfObject, children);
        }

        /*private void WriteHeader()
        {
            string prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
            string doctype = "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>\n";

            string header = prolog + doctype + @"<svg width=""" + width + @""" xmlns:xlink=""http://www.w3.org/1999/xlink"" " +
@" contentStyleType=""text/css"" viewBox=""0 0 " + width + @" " + height + @""" height=""" + height +
@"""  xmlns=""http://www.w3.org/2000/svg"" version=""1.0"">
";
             
        }*/

    }
}
